package com.icarsaas.saas.component.fileUpload;

import com.icarsaas.saas.common.exception.ResultException;
import com.icarsaas.saas.common.utils.SpringContextUtil;
import com.icarsaas.saas.common.utils.ToolUtil;
import com.icarsaas.saas.component.fileUpload.config.properties.UploadProjectProperties;
import com.icarsaas.saas.component.fileUpload.enums.UploadResultEnum;
import com.icarsaas.saas.component.shiro.ShiroUtil;
import com.icarsaas.saas.tenant.system.model.FileUpload;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
import java.io.*;
import java.math.BigInteger;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 文件上传处理工具
 *
 * @author chlingm
 *
 */
public class FileUploadUtil {

    /**
     * 创建一个Upload实体对象
     * @param multipartFile MultipartFile对象
     * @param modulePath 文件模块路径
     */
    public static FileUpload getFile(MultipartFile multipartFile, String refId, String type, String modulePath) {
        if (multipartFile.getSize() == 0) {
            throw new ResultException(UploadResultEnum.NO_FILE_NULL);
        }
        FileUpload upload = new FileUpload();
        upload.setRefId(refId);
        upload.setType(type);
        upload.setMime(multipartFile.getContentType());
        upload.setSize(multipartFile.getSize());
        String fileName = multipartFile.getOriginalFilename();
        String phyName = FileUploadUtil.genFileName(fileName);
        upload.setName(fileName);
        upload.setPhyName(phyName);
        // 路径应该包括
        String tenant = ShiroUtil.getSubject() == null? "/common" : ("/" + ShiroUtil.getSubject().getTenant());
//        upload.setPath(getPathPattern() + "/" + modulePath + FileUploadUtil.genDateMkdir("yyyyMMdd") + phyName);
        if (!modulePath.startsWith("/")) modulePath += "/";
        upload.setPath(tenant + modulePath + FileUploadUtil.genDateMkdir("yyyyMMdd") + phyName);
        return upload;
    }

    /**
     * 判断文件是否为支持的格式
     * @param multipartFile MultipartFile对象
     * @param types 支持的文件类型数组
     */
    public static boolean isContentType(MultipartFile multipartFile, String[] types){
        List<String> typeList = Arrays.asList(types);
        return typeList.contains(multipartFile.getContentType());
    }

    /**
     * 获取文件上传保存路径
     */
    public static String getUploadPath(){
        UploadProjectProperties properties = SpringContextUtil.getBean(UploadProjectProperties.class);
        return properties.getFilePath();
    }

    /**
     * 获取文件上传目录的静态资源路径
     */
    public static String getPathPattern(){
        UploadProjectProperties properties = SpringContextUtil.getBean(UploadProjectProperties.class);
        return properties.getStaticPath().replace("/**", "");
    }

    /**
     * 生成随机且唯一的文件名
     */
    public static String genFileName(String originalFilename){
        String fileSuffix = ToolUtil.getFileSuffix(originalFilename);
        return UUID.randomUUID().toString().replace("-", "") + fileSuffix;
    }

    /**
     * 生成指定格式的目录名称(日期格式)
     */
    public static String genDateMkdir(String format){
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return "/" + sdf.format(new Date()) + "/";
    }

    /**
     * 获取目标文件对象
     * @param upload 上传实体类
     */
    public static File getDestFile(FileUpload upload) throws IOException {

        // 创建保存文件对象
        String path = upload.getPath().replace(getPathPattern(), "");
        String filePath = getUploadPath() + path;
        File dest = new File(filePath.replace("//", "/"));
        if(!dest.exists()){
            dest.getParentFile().mkdirs();
            dest.createNewFile();
        }

        return dest;
    }

    /**
     * 保存文件及获取文件MD5值和SHA1值
     * @param multipartFile MultipartFile对象
     * @param upload Upload
     */
    public static void transferTo(MultipartFile multipartFile, FileUpload upload) throws IOException, NoSuchAlgorithmException {

        byte[] buffer = new byte[4096];
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        try (OutputStream fos = Files.newOutputStream(getDestFile(upload).toPath()); InputStream fis = multipartFile.getInputStream()) {
            int len = 0;
            while ((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
                md5.update(buffer, 0, len);
                sha1.update(buffer, 0, len);
            }
            fos.flush();
        }
//        BigInteger md5Bi = new BigInteger(1, md5.digest());
//        upload.setMd5(md5Bi.toString(16));
    }

    /**
     * 获取文件的SHA1值
     */
    public static String getFileSha1(MultipartFile multipartFile) {
        if (multipartFile.getSize() == 0){
            throw new ResultException(UploadResultEnum.NO_FILE_NULL);
        }
        byte[] buffer = new byte[4096];
        try (InputStream fis = multipartFile.getInputStream()) {
            MessageDigest sha1 = MessageDigest.getInstance("SHA1");
            int len = 0;
            while ((len = fis.read(buffer)) != -1) {
                sha1.update(buffer, 0, len);
            }
            BigInteger sha1Bi = new BigInteger(1, sha1.digest());
            return sha1Bi.toString(16);
        } catch (IOException | NoSuchAlgorithmException e) {
            return null;
        }
    }
    /**
     * 获取文件的SHA1值
     */
    public static String getFileMd5(MultipartFile multipartFile) {
        if (multipartFile.getSize() == 0){
            throw new ResultException(UploadResultEnum.NO_FILE_NULL);
        }
        byte[] buffer = new byte[4096];
        try (InputStream fis = multipartFile.getInputStream()) {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            int len = 0;
            while ((len = fis.read(buffer)) != -1) {
                md5.update(buffer, 0, len);
            }
            BigInteger sha1Bi = new BigInteger(1, md5.digest());
            return sha1Bi.toString(16);
        } catch (IOException | NoSuchAlgorithmException e) {
            return null;
        }
    }

    /**
     * 查找并返回完整文件路径，若找不到,则返回null</br>(检查的文件目录参见application.properties)
     * @param url_key application.properties设置的路径key, 当为null时，则通过配置文件逐一查找
     * @param file_url 文件路径, eg: 2014/12/22/14192131232329217.doc
     * @return 返回完整的文件路径 eg: /app/tender_zj/invitebid/file/2014/12/22/14192131232329217.doc
     */
    public static String getAllPath(String url_key, String file_url) {
        UploadProjectProperties properties = SpringContextUtil.getBean(UploadProjectProperties.class);
        File file = null;
        if (StringUtils.isNotEmpty(file_url)) {
            String allPath = properties.getFilePath() + file_url;
            file = new File(allPath);
            if (file.exists()) return allPath;
        }
        if (StringUtils.isNotEmpty(url_key)) {
            String allPath = SpringContextUtil.getEnvironmentProperty("url_key") + file_url;
            file = new File(allPath);
            return file.exists() ? allPath : null;
        } else {
            String[] paths = {"file", "image", "logo", "common", "avatar"};
            String allPath = "";
            for (String path : paths) {
                allPath = SpringContextUtil.getEnvironmentProperty("url_key") + file_url;
                file = new File(allPath);
                if (file.exists())
                    return allPath;// 如果存在则返回完整路径
            }
        }
        /*
        if (StringUtils.isNotEmpty(url_key)) {
            String allPath = properties.getFilePath() + file_url;
            file = new File(allPath);
            return file.exists() ? allPath : null;
        } else {// 否则逐一检测,存在则返回
            // 见application.properties
            String[] paths = {"file", "image", "logo", "common"};
            String allPath = "";
            for (String path : paths) {
                allPath = properties.getFilePath() + file_url;
//				try {
//					allPath = URLDecoder.decode(url + file_url, "UTF-8");
//				} catch (UnsupportedEncodingException e) {
//					e.printStackTrace();
//					return file_url;
//				}
                file = new File(allPath);
                if (file.exists())
                    return allPath;// 如果存在则返回完整路径
            }
        }
        */
        return null;
    }

    /**
     * 下载文件
     * @param request
     * @param response
     * @param fileModel
     * @throws Exception
     */
    public static void download(HttpServletResponse response, String path, String fileName) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            response.setContentType("text/html;charset=utf-8");
            File file = new File(path);
            if(!file.exists()) return;
            long fileLength = file.length();

            String realName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");

            response.setContentType("application/octet-stream");
            response.setHeader("Content-disposition", "attachment; filename=" + realName);
            response.setHeader("Content-Length", String.valueOf(fileLength));

            bis = new BufferedInputStream(new FileInputStream(path));
            bos = new BufferedOutputStream(response.getOutputStream());
            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
            bis.close();
            bos.close();
        } catch (Exception e) {
            if (bis != null)
                try {
                    bis.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            if (bos != null)
                try {
                    bos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
        }
    }

}
